package com.hxk.shiro.config;

import com.hxk.shiro.bean.User;
import org.apache.commons.collections.map.LinkedMap;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.*;
import org.apache.shiro.authz.AuthorizationInfo;
import org.apache.shiro.authz.SimpleAuthorizationInfo;
import org.apache.shiro.mgt.DefaultSecurityManager;
import org.apache.shiro.realm.AuthorizingRealm;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.mgt.WebSecurityManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;

/**
 * shiro 配置类
 *  根据shiro官网提供的三大核心类
 *      subject（当前用户），
 *      SecurityManager（安器管理器）
 *      Realm （数据交互登录和授权的核心类）
 *  那么我们也要在这里配置以上类：
 *      subject（当前用户），不需要配置，shiro已经提供了工具类获取Subject currentUser = SecurityUtils.getSubject();
 *      SecurityManager（安器管理器），需要配置
 *      Realm （数据交互登录和授权的核心类），需要配置
 *      ShiroFilterFactoryBean 这里还有增加一个配置类，shiro的url过滤器
 */
@Configuration
public class ShiroConfig {

    /**
     * url过滤器
     *  配置缩写	                对应的过滤器	                            功能
         anon	            AnonymousFilter	                    指定url可以匿名访问
         authc	            FormAuthenticationFilter	        指定url需要form表单登录，默认会从请求中获取username、password,rememberMe等参数并尝试登录，如果登录不了就会跳转到loginUrl配置的路径。我们也可以用这个过滤器做默认的登录逻辑，但是一般都是我们自己在控制器写登录逻辑的，自己写的话出错返回的信息都可以定制嘛。
         authcBasic	        BasicHttpAuthenticationFilter	    指定url需要basic登录
         logout	            LogoutFilter	                    登出过滤器，配置指定url就可以实现退出功能，非常方便
         noSessionCreation	NoSessionCreationFilter	            禁止创建会话
         perms	            PermissionsAuthorizationFilter	    需要指定权限才能访问
         port	            PortFilter	                        需要指定端口才能访问
         rest	            HttpMethodPermissionFilter	        将http请求方法转化成相应的动词来构造一个权限字符串，这个感觉意义不大，有兴趣自己看源码的注释
         roles	            RolesAuthorizationFilter	        需要指定角色才能访问
         ssl	            SslFilter	                        需要https请求才能访问
         user	            UserFilter	                        需要已登录或“记住我”的用户才能访问
     * @return
     */
    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(WebSecurityManager webSecurityManager){
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(webSecurityManager);
        //配置请求拦截
        Map<String, String> filterMap = new LinkedHashMap<>();
        filterMap.put("/index", "anon");//首页请求不需要认证
        filterMap.put("/toLogin", "anon");//登录页面请求不需要认证
        filterMap.put("/js/**", "anon");//js路径请求都不需要认证
        filterMap.put("/user/**", "authc");//其他系统中所有的请求都必须认证
        //设置登出
        filterMap.put("/logout", "logout");
        shiroFilterFactoryBean.setFilterChainDefinitionMap(filterMap);
        //开启登录
        shiroFilterFactoryBean.setLoginUrl("/toLogin");
        //设置未授权页面
        shiroFilterFactoryBean.setUnauthorizedUrl("/noauth");

        return shiroFilterFactoryBean;
    }

    /**
     * 安器管理器
     * @return
     */
    @Bean
    public WebSecurityManager webSecurityManager(){
        return new DefaultWebSecurityManager(userRealm());
    }

    /**
     * 数据交互登录和授权的核心类
     * @return
     */
    @Bean
    public AuthorizingRealm userRealm(){
        return new AuthorizingRealm() {
            //授权
            @Override
            protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principal) {
                System.out.println("shiro 授权--->>doGetAuthorizationInfo");

                //获取当前用户
                User user = (User) SecurityUtils.getSubject().getPrincipal();

                SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo();
                // 角色
                Set<String> roles = new HashSet<>();
                // 页面使用
                Set<String> permissions = new HashSet<>();
                // 权限
                // 测试用权限
                if ("admin".equals(user.getUsername())) {
                    roles.add("admin");
                    permissions.add("op:write");
                } else {
                    roles.add("user");
                    permissions.add("op:read");
                }
                authorizationInfo.setRoles(roles);
                authorizationInfo.setStringPermissions(permissions);
                return authorizationInfo;
            }

            //认证
            @Override
            protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException {
                System.out.println("shiro 认证--->>doGetAuthenticationInfo");

                if(token != null){
                    //数据库查询获取
                    User user = new User("hxk","123456");
                    UsernamePasswordToken userToken = (UsernamePasswordToken) token;

                    if(!userToken.getUsername().equals(user.getUsername())){
                        return null;
                    }

                    return new SimpleAuthenticationInfo(user,user.getPassword(),user.getUsername());
                }

                return null;
            }
        };
    }
}
